// -----------------------------------------------------------------------------
// This source file is part of Matrix Platform
// 	(Universal .NET Software Development Platform)
// For the latest info, see http://www.matrixplatform.com
// 
// Copyright (c) 2009-2010, Ingenious Ltd
// 
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
// 
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
// 
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
// -----------------------------------------------------------------------------
using System;
using System.Collections.Generic;
using System.Text;
using System.Reflection;
using System.Threading;
using Matrix.Common.Core;
using Matrix.Common.Diagnostics.TracerCore.Items;

namespace Matrix.Common.Diagnostics.TracerCore
{
    /// <summary>
    /// Class generates tracer items.
    /// 
    /// </summary>
    public class TracerItemBuilder : ITracerItemBuilder
    {
        public const TracerItem.PriorityEnum DefaultReportPriority = TracerItem.PriorityEnum.Trivial;

        volatile bool _enabled = true;
        /// <summary>
        /// Enabled / disable operation of the tracer.
        /// </summary>
        public bool Enabled
        {
            get { return _enabled; }
            set { _enabled = value; }
        }

        volatile int _methodInfoLoopback = 2;

        /// <summary>
        /// How many method to go up the stack when printing method info on trace items.
        /// </summary>
        public int MethodInfoLoopback
        {
            get { return _methodInfoLoopback; }
            set { _methodInfoLoopback = value; }
        }

        bool _extractMethodInformation = true;
        /// <summary>
        /// Provides information on the method that traced,
        /// but is rather slow (max speed 30-50K per second).
        /// </summary>
        public bool ExtractMethodInformation
        {
            get { return _extractMethodInformation; }
            set { _extractMethodInformation = value; }
        }

        public delegate void ItemUpdateDelegate(TracerItemBuilder generator, TracerItem item);
        public event ItemUpdateDelegate ItemCreatedEvent;

        /// <summary>
        /// Constructor.
        /// </summary>
        public TracerItemBuilder()
        {
        }

        public virtual void Dispose()
        {
            ItemCreatedEvent = null;
        }

        /// <summary>
        /// Full feature call, no tracer.
        /// </summary>
        /// <param name="tracer"></param>
        /// <param name="type"></param>
        /// <param name="priority"></param>
        /// <param name="message"></param>
        protected virtual TracerItem CreateItem(bool extractMethodInformation, InstanceMonitor monitor,
            TracerItem.TypeEnum type, TracerItem.ExtendedTypeEnum extendedType, 
            TracerItem.PriorityEnum priority, string message, Exception exception)
        {
            if (Enabled == false)
            {
                return null;
            }

            TracerItem item;

            if (extractMethodInformation)
            {// Method detailed item.
                string threadId = Thread.CurrentThread.ManagedThreadId.ToString();
                string threadName = Thread.CurrentThread.Name;
                MethodBase method = ReflectionHelper.GetExternalCallingMethod(_methodInfoLoopback, null);

                item = new MethodTracerItem(type, extendedType,  priority, message, string.Empty, method);
            }
            else
            {// Conventional item.
                item = new TracerItem(type, extendedType, priority, message, string.Empty);
            }

            item.InstanceMonitor = monitor;
            item.Exception = exception;

            ItemUpdateDelegate del = ItemCreatedEvent;
            if (del != null)
            {
                del(this, item);
            }

            return item;
        }

        #region Public

        public TracerItem Debug(string reportMessage)
        {
            return CreateItem(_extractMethodInformation, null, TracerItem.TypeEnum.Debug, TracerItem.ExtendedTypeEnum.None, DefaultReportPriority, reportMessage, null);
        }

        public TracerItem Debug(string reportMessage, Exception exception)
        {
            return CreateItem(_extractMethodInformation, null, TracerItem.TypeEnum.Debug, TracerItem.ExtendedTypeEnum.None, DefaultReportPriority, reportMessage, exception);
        }

        /// <summary>
        /// 
        /// </summary>
        public TracerItem Info(string reportMessage)
        {
            return CreateItem(_extractMethodInformation, null, TracerItem.TypeEnum.Info, TracerItem.ExtendedTypeEnum.None, DefaultReportPriority, reportMessage, null);
        }

        /// <summary>
        /// 
        /// </summary>
        public TracerItem ReportImportant(string reportMessage)
        {
            return CreateItem(_extractMethodInformation, null, TracerItem.TypeEnum.Info, TracerItem.ExtendedTypeEnum.None, TracerItem.PriorityEnum.High, reportMessage, null);
        }

        /// <summary>
        /// Report a simplme message to the report management system.
        /// </summary>
        /// <param name="reportMessage"></param>
        public TracerItem Info(string reportMessage, TracerItem.PriorityEnum priority)
        {
            return CreateItem(_extractMethodInformation, null, TracerItem.TypeEnum.Info, TracerItem.ExtendedTypeEnum.None, priority, reportMessage, null);
        }
        
        /// <summary>
        /// Put a system report if condition is met.
        /// </summary>
        public TracerItem InfoIf(bool condition, string reportMessage)
        {
            if (condition)
            {
                return CreateItem(_extractMethodInformation, null, TracerItem.TypeEnum.Info, TracerItem.ExtendedTypeEnum.None, DefaultReportPriority, reportMessage, null);
            }

            return null;
        }

        /// <summary>
        /// Report operation warning; it is a normal occurence in the work of the system. It can be caused
        /// for example by the lack of access to a resource or some error in a data stream.
        /// </summary>
        /// <param name="warningMessage"></param>
        public TracerItem OperationWarning(string warningMessage)
        {
            return CreateItem(_extractMethodInformation, null, TracerItem.TypeEnum.Warning, TracerItem.ExtendedTypeEnum.Operation,
                              TracerItem.PriorityEnum.High, warningMessage, null);
        }

        /// <summary>
        /// 
        /// </summary>
        public TracerItem OperationWarning(string warningMessage, TracerItem.PriorityEnum priority)
        {
            return CreateItem(_extractMethodInformation, null, TracerItem.TypeEnum.Warning, TracerItem.ExtendedTypeEnum.Operation,
                              priority, warningMessage, null);
        }

        /// <summary>
        /// Chech given condition, if false, report operation warning with given message.
        /// Operation warnings are a lighter version of a warning, and may be expected to 
        /// occur during normal operaiton of the application.
        /// </summary>
        public TracerItem OperationWarningIf(bool condition, string message)
        {
            if (condition)
            {
                return CreateItem(_extractMethodInformation, null, TracerItem.TypeEnum.Warning, TracerItem.ExtendedTypeEnum.Operation,
                                  TracerItem.PriorityEnum.High, message, null);
            }

            return null;
        }

        public TracerItem OperationWarningIf(bool condition, string message, TracerItem.PriorityEnum priority)
        {
            if (condition)
            {
                return CreateItem(_extractMethodInformation, null, TracerItem.TypeEnum.Warning, TracerItem.ExtendedTypeEnum.Operation,
                                  priority, message, null);
            }

            return null;
        }


        /// <summary>
        /// A Warning notifies that in some part of the systems operation a recovarable error has occured.
        /// </summary>
        /// <param name="warningMessage"></param>
        public TracerItem Warning(string warningMessage)
        {
            return CreateItem(_extractMethodInformation, null, TracerItem.TypeEnum.Warning, TracerItem.ExtendedTypeEnum.None, TracerItem.PriorityEnum.High, warningMessage, null);
        }

        /// <summary>
        /// Report a piece of code is not implemented, however missing functionality is not critical to operation.
        /// </summary>
        /// <param name="parameters"></param>
        public TracerItem NotImplementedWarning(params string[] parameters)
        {
            return CreateItem(_extractMethodInformation, null, TracerItem.TypeEnum.Warning, TracerItem.ExtendedTypeEnum.None, TracerItem.PriorityEnum.High,
                              "Warning: Not Implemented [" + CommonHelper.ParamsToString(parameters) + "]", null);
        }

        /// <summary>
        /// Trace warning in case condition is met.
        /// </summary>
        public TracerItem WarningIf(bool condition, params string[] parameters)
        {
            if (condition)
            {
                return CreateItem(_extractMethodInformation, null, TracerItem.TypeEnum.Warning, TracerItem.ExtendedTypeEnum.None, TracerItem.PriorityEnum.High, "Warning: " + CommonHelper.ParamsToString(parameters), null);
            }

            return null;
        }

        /// <summary>
        /// Report operation error with exception.
        /// </summary>
        /// <param name="ex"></param>
        public TracerItem OperationError(string errorDetails, Exception exception)
        {
            return CreateItem(_extractMethodInformation, null, TracerItem.TypeEnum.Error, TracerItem.ExtendedTypeEnum.Operation, 
                              TracerItem.PriorityEnum.High, errorDetails, exception);
        }

        /// <summary>
        /// Report operation error; it is a lighter, more common version of a error, and may be expected to 
        /// occur during normal operation of the application (for. ex. a given non critical 
        /// resource was not retrieved, operation has timed out etc.)
        /// </summary>
        /// <param name="errorMessage"></param>
        public TracerItem OperationError(string errorMessage)
        {
            return CreateItem(_extractMethodInformation, null,
                              TracerItem.TypeEnum.Error, TracerItem.ExtendedTypeEnum.Operation, TracerItem.PriorityEnum.High, errorMessage, null);
        }

        /// <summary>
        /// 
        /// </summary>
        public TracerItem OperationError(string errorMessage, TracerItem.PriorityEnum priority)
        {
            return CreateItem(_extractMethodInformation, null,
                              TracerItem.TypeEnum.Error, TracerItem.ExtendedTypeEnum.Operation, priority, errorMessage, null);
        }

        /// <summary>
        /// Check given condition, if false, report operation error with given message.
        /// Operation errors are a lighter version of a error, and may be expected to 
        /// occur during normal operation of the application (for. ex. a given non critical 
        /// resource was not retrieved, operation has timed out etc.)
        /// </summary>
        public TracerItem OperationErrorIf(bool condition, string errorMessage)
        {
            if (condition)
            {
                return OperationError(errorMessage);
            }

            return null;
        }

        /// <summary>
        /// 
        /// </summary>
        public TracerItem Fatal(string errorMessage)
        {
            return CreateItem(_extractMethodInformation, null, TracerItem.TypeEnum.Error, TracerItem.ExtendedTypeEnum.None,
                              TracerItem.PriorityEnum.Critical, errorMessage, null);
        }

        public TracerItem Fatal(string errorMessage, Exception exception)
        {
            return CreateItem(_extractMethodInformation, null, TracerItem.TypeEnum.Fatal, TracerItem.ExtendedTypeEnum.None, 
                              TracerItem.PriorityEnum.Critical, errorMessage, exception);
        }

        /// <summary>
        /// Report an serious error. Those errors are usually a sign something in the work
        /// of the application has gone seriously wrong, and operation can not continue
        /// properly (for ex. unexpected exception, access to critical resources etc.)
        /// </summary>
        /// <param name="errorMessage"></param>
        public TracerItem Error(string errorMessage)
        {
            return CreateItem(_extractMethodInformation, null, TracerItem.TypeEnum.Error, TracerItem.ExtendedTypeEnum.None, 
                              TracerItem.PriorityEnum.High, errorMessage, null);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="errorMessage"></param>
        public TracerItem Error(string errorMessage, TracerItem.PriorityEnum priority)
        {
            return CreateItem(_extractMethodInformation, null, TracerItem.TypeEnum.Error, TracerItem.ExtendedTypeEnum.None,
                              priority, errorMessage, null);
        }

        /// <summary>
        /// Helper, redefine with exception consumption.
        /// </summary>
        /// <param name="errorMessage"></param>
        /// <param name="exception"></param>
        public TracerItem Error(string errorMessage, Exception exception)
        {
            return CreateItem(_extractMethodInformation, null, TracerItem.TypeEnum.Error, TracerItem.ExtendedTypeEnum.None, TracerItem.PriorityEnum.High, errorMessage, exception);
        }

        /// <summary>
        /// </summary>
        /// <param name="condition"></param>
        /// <param name="errorMessage"></param>
        public TracerItem ErrorIf(bool condition)
        {
            if (condition)
            {
                return CreateItem(_extractMethodInformation, null, TracerItem.TypeEnum.Error, TracerItem.ExtendedTypeEnum.None, TracerItem.PriorityEnum.High, string.Empty, null);
            }

            return null;
        }

        /// <summary>
        /// Perform a check on a condition. If the conditions is *false* set error with specified message.
        /// </summary>
        /// <param name="condition"></param>
        /// <param name="errorMessage"></param>
        public TracerItem ErrorIf(bool condition, string errorMessage)
        {
            if (condition)
            {
                return CreateItem(_extractMethodInformation, null, TracerItem.TypeEnum.Error, TracerItem.ExtendedTypeEnum.None, TracerItem.PriorityEnum.High, errorMessage, null);
            }

            return null;
        }

        /// <summary>
        /// Report a piece of code is not implemented.
        /// </summary>
        /// <param name="parameters"></param>
        public TracerItem NotImplementedError(params string[] parameters)
        {
            return CreateItem(_extractMethodInformation, null, TracerItem.TypeEnum.Error, TracerItem.ExtendedTypeEnum.None , TracerItem.PriorityEnum.High, 
                              "Error: Not Implemented [" + CommonHelper.ParamsToString(parameters) + "]", null);
        }

        public TracerItem MethodEntry()
        {
            return CreateItem(_extractMethodInformation, null, TracerItem.TypeEnum.Debug, TracerItem.ExtendedTypeEnum.MethodEntry, TracerItem.PriorityEnum.Default, string.Empty, null);
        }

        public TracerItem MethodEntry(string message)
        {
            return CreateItem(_extractMethodInformation, null, TracerItem.TypeEnum.Debug, TracerItem.ExtendedTypeEnum.MethodEntry, TracerItem.PriorityEnum.Default, message, null);
        }

        public TracerItem MethodExit()
        {
            return CreateItem(_extractMethodInformation, null, TracerItem.TypeEnum.Debug, TracerItem.ExtendedTypeEnum.MethodExit, TracerItem.PriorityEnum.Default, string.Empty, null);
        }

        public TracerItem MethodExit(string message)
        {
            return CreateItem(_extractMethodInformation, null, TracerItem.TypeEnum.Debug, TracerItem.ExtendedTypeEnum.MethodExit, TracerItem.PriorityEnum.Default, message, null);
        }

        #endregion

        #region Public Traces

        public void TraceFast(TracerItem.TypeEnum itemType, TracerItem.ExtendedTypeEnum itemExtendedType, string message)
        {
            CreateItem(false, null, itemType, itemExtendedType, TracerItem.PriorityEnum.Default, message, null);
        }

        //public TracerItem Trace(string message)
        //{
        //    return CreateItem(_extractMethodInformation, null, TracerItem.TypeEnum.Debug, TracerItem.ExtendedTypeEnum.None,  TracerItem.PriorityEnum.Default, message, null);
        //}

        //public TracerItem Trace(string message, TracerItem.PriorityEnum priority)
        //{
        //    return CreateItem(_extractMethodInformation, null, TracerItem.TypeEnum.Debug, TracerItem.ExtendedTypeEnum.None, priority, message, null);
        //}

        #endregion


    }
}
